// FindResultDialog.cpp : implementation file
//

#include "stdafx.h"
#include "FindResultDialog.h"
#include "afxdialogex.h"
#include "plugin.h"

unsigned __stdcall thread_func( void * ptr )
{
	FindResultDialog * ptr_obj = ( FindResultDialog * )ptr;
	ptr_obj->search_thread();

	return 0;
}

// FindResultDialog dialog

IMPLEMENT_DYNAMIC(FindResultDialog, CDialogEx)

FindResultDialog::FindResultDialog(CWnd* pParent /*=NULL*/)
	: CDialogEx(FindResultDialog::IDD, pParent)
	, m_is_stop(false)
	, m_hthread(0)
{

}

FindResultDialog::~FindResultDialog()
{
}

void FindResultDialog::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST, m_list);
	DDX_Control(pDX, IDC_PROGRESS1, m_searching_progress);
}

void FindResultDialog::start_search( TCHAR * ptr_str , BOOL is_matchcase , BOOL is_useregex )
{
	memset( m_searchstr , 0 , sizeof( m_searchstr ) );
	wcscpy_s( m_searchstr , ptr_str );

	m_is_matchcase = is_matchcase;
	m_is_useregex = is_useregex;

	do_search();
}

void FindResultDialog::do_search()
{
	stop_search();

	DWORD id;
	m_hthread = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) thread_func, this, 0, &id);
}

void FindResultDialog::stop_search()
{
	m_is_stop = true;

	if( m_hthread )
	{
		WaitForSingleObject( m_hthread , INFINITE );
		CloseHandle( m_hthread );
	}
	
	m_hthread = 0;

	m_is_stop = false;
}

void FindResultDialog::search_thread()
{
	::ShowWindow( ::GetDlgItem( m_hWnd , IDC_PROGRESS1 ) , SW_SHOW );
	::SendMessage( ::GetDlgItem( m_hWnd , IDC_PROGRESS1 ) , PBM_SETRANGE32 , 0 , 100 );

	UINT_PTR nLines = Editor_GetLines( m_parentwnd, POS_LOGICAL_W );


	GET_LINE_INFO gli;
	gli.flags = FLAG_LOGICAL;

	wchar_t * ptr_str = 0;
	UINT_PTR str_size = 0;

	for( UINT_PTR i = 0 ; i < nLines && !m_is_stop ; i++ )
	{
		gli.cch = 0;
		gli.yLine = i;

		UINT_PTR size = Editor_GetLineW( m_parentwnd , &gli , ptr_str );

		if( size > str_size )
		{
			delete []ptr_str;
			ptr_str = new wchar_t[ size * 2 ];
			memset( ptr_str , 0 , size * 2 * sizeof( wchar_t ) );
			str_size = size * 2;
		}
		else
		{
			memset( ptr_str , 0 , str_size * sizeof( wchar_t ) );
		}

		gli.cch = str_size;

		Editor_GetLineW( m_parentwnd , &gli , ptr_str );

		if( search_line( ptr_str ) )
		{
			TCHAR linestring[32];
			memset( linestring , 0 , sizeof(linestring) );
			int linestring_len = 0;

#ifdef _WIN64
			linestring_len = swprintf( linestring, sizeof(linestring) / sizeof(TCHAR) , _T("%I64u"), gli.yLine + 1 );
#else
			linestring_len = swprintf( linestring, sizeof(linestring) / sizeof(TCHAR) , _T("%u"), gli.yLine + 1 );
#endif

			HWND hlist = ::GetDlgItem( m_hWnd , IDC_LIST );
			LVITEM item;
			ZeroMemory( &item, sizeof( item ) );
			item.mask = LVIF_TEXT;
			item.iItem = INT_MAX;
			item.pszText = linestring;
			item.cchTextMax = linestring_len + 1;
			int iItem = ListView_InsertItem( hlist, &item );

			if( iItem >= 0 )
			{
				item.iItem = iItem;
				item.iSubItem = 1;
				item.pszText = ptr_str;
				item.cchTextMax = wcslen( ptr_str ) + 1;
				ListView_SetItem( hlist, &item );
			}
		}

		int pos = (int)(i * 100. / nLines);

		::SendMessage( ::GetDlgItem( m_hWnd , IDC_PROGRESS1 ) , PBM_SETPOS , pos , 0 );
	}

	delete []ptr_str;

	::ShowWindow( ::GetDlgItem( m_hWnd , IDC_PROGRESS1 ) , SW_HIDE );
}

wchar_t * wcsnistr_fastwithfirstchr( wchar_t * ptr_str , wchar_t * ptr_searchstr , wchar_t firstchr )
{
	while( *ptr_str )
	{
		ptr_str = wcschr( ptr_str , firstchr );

		if( ptr_str )
		{
			int ret = _wcsnicmp( ptr_str , ptr_searchstr , wcslen( ptr_searchstr ) );

			if( ret == 0 )
				return ptr_str;
			else
			{
				ptr_str++;
			}
		}
		else
		{
			return 0;
		}
	}

	return 0;
}

wchar_t * wcsnistr( wchar_t * ptr_str , wchar_t * ptr_searchstr )
{
	wchar_t * ptr_pos = wcsnistr_fastwithfirstchr( ptr_str , ptr_searchstr , ptr_searchstr[0] );

	if( ptr_pos )
		return ptr_pos;

	wchar_t value = ptr_searchstr[0];

	if( ptr_searchstr[0] >= _T('A') && ptr_searchstr[0] <= _T('Z') )
		value = value + (_T('a') - _T('A'));
	else if( ptr_searchstr[0] >= _T('a') && ptr_searchstr[0] <= _T('z') )
		value = value - _T('a') + _T('A');

	if( value == ptr_searchstr[0] )
		return 0;
	else
	{
		return wcsnistr_fastwithfirstchr( ptr_str , ptr_searchstr , value );
	}
}

bool FindResultDialog::search_line( wchar_t * ptr_line )
{
	if( m_is_useregex )
	{
		LPCWSTR pszStart, pszEnd, pszNext;

		FIND_REGEX_INFO fri;
		ZeroMemory( &fri, sizeof( fri ) );
		fri.cbSize = sizeof( fri );
		fri.pszRegex = m_searchstr;
		fri.pszText = ptr_line;
		fri.ppszStart = &pszStart;
		fri.ppszEnd = &pszEnd;
		fri.ppszNext = &pszNext;
		//fri.nFlags = FLAG_FIND_NEXT;

		if( m_is_matchcase )
			fri.nFlags |= FLAG_FIND_CASE;

		int ret = Editor_FindRegex( m_parentwnd, &fri );

		if( ret == -1 )
			return false;
		else if( ret )
			return true;
		else
			return false;
	}
	else
	{
		if( m_is_matchcase )
		{
			if( wcsstr( ptr_line , m_searchstr ) )
				return true;
			else
			{
				return false;
			}
		}
		else
		{
			if( wcsnistr( ptr_line , m_searchstr ) )
				return true;
			else
			{
				return false;
			}
		}
	}
}


BEGIN_MESSAGE_MAP(FindResultDialog, CDialogEx)
	ON_WM_SIZE()
	ON_NOTIFY(NM_DBLCLK, IDC_LIST, &FindResultDialog::OnNMDblclkList)
	ON_WM_CLOSE()
END_MESSAGE_MAP()


// FindResultDialog message handlers


BOOL FindResultDialog::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	m_list.InsertColumn( 0 , _T("Line") , LVCFMT_LEFT , 40 );

	CRect  rc;
	m_list.GetClientRect(&rc);

	m_list.InsertColumn( 1 , _T("String") , LVCFMT_LEFT , rc.Width() - 40 -18 );

	DWORD dwFlags = LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP;
	m_list.SetExtendedStyle( dwFlags );

	CRect wndrc;
	GetClientRect( &wndrc );
	int cx = wndrc.Width();
	int cy = wndrc.Height();

	m_list.SetWindowPos( &wndTop , 7 , 7 , cx - 14 , cy - 14 , SWP_NOMOVE | SWP_NOZORDER );
	m_list.SetColumnWidth( 1 , cx - 14 - 40 - 18 );

	m_searching_progress.SetWindowPos( &wndTop , 7 , 2 , cx - 14 , 4 , SWP_NOMOVE | SWP_NOZORDER );

	return TRUE;  // return TRUE unless you set the focus to a control
}


void FindResultDialog::OnSize(UINT nType, int cx, int cy)
{
	CDialogEx::OnSize(nType, cx, cy);

	// TODO: Add your message handler code here
	//m_list.SetWindowPos( &wndTop , 7 , 7 , cx - 14 , cy - 14 , SWP_NOMOVE | SWP_NOZORDER );
	if( GetDlgItem( IDC_LIST ) )
	{
		m_list.SetWindowPos( &wndTop , 7 , 7 , cx - 14 , cy - 14 , SWP_NOMOVE | SWP_NOZORDER );
		m_list.SetColumnWidth( 1 , cx - 14 - 40 - 18 );
	}

	if( GetDlgItem( IDC_PROGRESS1 ) )
	{
		m_searching_progress.SetWindowPos( &wndTop , 7 , 2 , cx - 14 , 4 , SWP_NOMOVE | SWP_NOZORDER );
	}
}


void FindResultDialog::OnNMDblclkList(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
	// TODO: Add your control notification handler code here
	*pResult = 0;

	wchar_t str[32];
	memset( str , 0 , sizeof( str ) );

	LVITEM item;
	memset( &item , 0 , sizeof(LVITEM) );
	item.iItem = pNMItemActivate->iItem;
	item.iSubItem = 0;
	item.cchTextMax = 32;
	item.pszText = str;
	item.mask = LVIF_TEXT;

	m_list.GetItem( &item );

	long long linenum = _wtoi64( str );

	POINT_PTR ptPos;
	ptPos.x = 0;
	ptPos.y = linenum - 1;
	Editor_SetCaretPos( m_parentwnd, POS_LOGICAL_W, &ptPos );

	UINT nFlags = FLAG_FIND_NEXT;

	if( m_is_matchcase )
		nFlags |= FLAG_FIND_CASE;

	if( m_is_useregex )
		nFlags |= FLAG_FIND_REG_EXP;

	Editor_FindW( m_parentwnd, nFlags, m_searchstr );
}


void FindResultDialog::OnClose()
{
	// TODO: Add your message handler code here and/or call default

	m_is_stop = true;

	CDialogEx::OnClose();
}
